home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.0 / stk-3 / blt-for-STk-3.0 / blt-1.9 / src / bltBusy.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-12-19  |  25.7 KB  |  841 lines

  1. /*
  2.  * bltBusy.c --
  3.  *
  4.  *    This module implements busy windows for the Tk toolkit.
  5.  *
  6.  * Copyright 1993-1994 by AT&T Bell Laboratories.
  7.  * Permission to use, copy, modify, and distribute this software
  8.  * and its documentation for any purpose and without fee is hereby
  9.  * granted, provided that the above copyright notice appear in all
  10.  * copies and that both that the copyright notice and warranty
  11.  * disclaimer appear in supporting documentation, and that the
  12.  * names of AT&T Bell Laboratories any of their entities not be used
  13.  * in advertising or publicity pertaining to distribution of the
  14.  * software without specific, written prior permission.
  15.  *
  16.  * AT&T disclaims all warranties with regard to this software, including
  17.  * all implied warranties of merchantability and fitness.  In no event
  18.  * shall AT&T be liable for any special, indirect or consequential
  19.  * damages or any damages whatsoever resulting from loss of use, data
  20.  * or profits, whether in an action of contract, negligence or other
  21.  * tortuous action, arising out of or in connection with the use or
  22.  * performance of this software.
  23.  *
  24.  * Busy command created by George Howlett.
  25.  */
  26.  
  27. #include "blt.h"
  28. #include <X11/Xutil.h>
  29. #include <X11/Xatom.h>
  30.  
  31. #if (TK_MINOR_VERSION > 0)
  32. #define Cursor Tk_Cursor
  33. #endif
  34.  
  35. #ifndef BUSY_VERSION
  36. #define BUSY_VERSION "1.2"
  37. #endif
  38.  
  39. typedef struct {
  40.     Tk_Window host;        /* Host window of the busy window. It is used
  41.                  * to manage the size and position of the busy
  42.                  * window. */
  43.     unsigned int width, height;    /* Last known size of the host window. Also
  44.                  * specifies the size of the busy window. */
  45.     int hostX, hostY;        /* Last known position of the host window */
  46.     int x, y;            /* Position of the busy window in its parent */
  47.     Tk_Window mainWin;        /* Used to key searches in the window hierarchy
  48.                  * See the "hosts" command. */
  49.     int mapped;            /* If non-zero, busy window is mapped */
  50.     Window window;        /* Busy window: InputOnly class window */
  51.     Display *display;        /* Display of parent, host, and busy windows */
  52.  
  53.     Cursor cursor;        /* Cursor for the busy window */
  54.     Tk_Window parent;        /* Parent window of the busy window. It may be
  55.                      * the host window or a mutual ancestor of the
  56.                  * host window */
  57.  
  58. } BusyWin;
  59.  
  60. #define TRUE        1
  61. #define FALSE        0
  62. #define DEF_BUSY_CURSOR "watch"
  63.  
  64. static int ParseParent _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp,
  65.     Tk_Window tkwin, char *value, char *widgRec, int offset));
  66. static char *PrintParent _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin,
  67.     char *widgRec, int offset, Tcl_FreeProc **freeProcPtr));
  68.  
  69. static Tk_CustomOption ParentOption =
  70. {
  71.     ParseParent, PrintParent, (ClientData)0
  72. };
  73.  
  74. static Tk_ConfigSpec configSpecs[] =
  75. {
  76.     {TK_CONFIG_CURSOR, "-cursor", "busyCursor", "BusyCursor",
  77.     DEF_BUSY_CURSOR, Tk_Offset(BusyWin, cursor), TK_CONFIG_NULL_OK},
  78.     {TK_CONFIG_CUSTOM, "-in", (char *)NULL, (char *)NULL, (char *)NULL,
  79.     Tk_Offset(BusyWin, parent), TK_CONFIG_NULL_OK, &ParentOption},
  80.     {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0}
  81. };
  82.  
  83. static int initialized = 0;    /* Flag to initialize the hash table */
  84. static Tcl_HashTable busyWinTable;    /* Hash table of busy window structures keyed
  85.                    * by the address of the host Tk window */
  86.  
  87. /* Forward declarations */
  88. static void DestroyBusyWindow _ANSI_ARGS_((ClientData clientData));
  89.  
  90. /*
  91.  *----------------------------------------------------------------------
  92.  *
  93.  * ParseParent --
  94.  *
  95.  *    Convert the pathname of a Tk window and check to see if its a
  96.  *    valid parent for the busy window.
  97.  *
  98.  *     A valid parent window is either
  99.  *
  100.  *            1) the host window itself, or
  101.  *         2) a mutual ancestor of the host window.
  102.  *
  103.  *
  104.  *----------------------------------------------------------------------
  105.  */
  106. /*ARGSUSED*/
  107. static int
  108. ParseParent(clientData, interp, tkwin, value, widgRec, offset)
  109.     ClientData clientData;    /* not used */
  110.     Tcl_Interp *interp;        /* Interpreter to send results back to */
  111.     Tk_Window tkwin;        /* Used to search for window */
  112.     char *value;        /* Pathname of new parent window */
  113.     char *widgRec;        /* BusyWin structure record */
  114.     int offset;            /* not used */
  115. {
  116.     BusyWin *busyPtr = (BusyWin *)(widgRec);
  117.     Tk_Window parent;
  118.     int x, y;
  119.  
  120.     x = y = 0;
  121.     if (value == NULL) {
  122.     parent = busyPtr->host;
  123.     } else {
  124.     parent = Tk_NameToWindow(interp, value, tkwin);
  125.     if (parent == NULL) {
  126.         return TCL_ERROR;
  127.     }
  128.     if (parent != busyPtr->host) {
  129.         Tk_Window ancestor;
  130.  
  131.         for (ancestor = busyPtr->host; ancestor != parent;
  132.         ancestor = Tk_Parent(ancestor)) {
  133.         x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width;
  134.         y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width;
  135.         if (Tk_IsTopLevel(ancestor)) {
  136.             break;
  137.         }
  138.         }
  139.         if (ancestor != parent) {
  140.         Tcl_AppendResult(interp, "\"", Tk_PathName(parent),
  141.             "\" in not a parent of \"", Tk_PathName(busyPtr->host),
  142.             "\"", (char *)NULL);
  143.         return TCL_ERROR;
  144.         }
  145.     }
  146.     }
  147.     if (parent != busyPtr->parent) {
  148.     if (busyPtr->window != None) {
  149.         XReparentWindow(busyPtr->display, busyPtr->window,
  150.         Tk_WindowId(parent), x, y);
  151.         XMapRaised(busyPtr->display, busyPtr->window);
  152.     }
  153.     busyPtr->parent = parent;
  154.     busyPtr->x = x;
  155.     busyPtr->y = y;
  156.     }
  157.     return TCL_OK;
  158. }
  159.  
  160. /*
  161.  *----------------------------------------------------------------------
  162.  *
  163.  * PrintParent --
  164.  *
  165.  *    Returns the path name of the parent window for busy window
  166.  *
  167.  * Results:
  168.  *    The path name of the parent of the busy window.
  169.  *
  170.  *----------------------------------------------------------------------
  171.  */
  172. /*ARGSUSED*/
  173. static char *
  174. PrintParent(clientData, tkwin, widgRec, offset, freeProcPtr)
  175.     ClientData clientData;    /* not used */
  176.     Tk_Window tkwin;        /* not used */
  177.     char *widgRec;        /* BusyWin structure record */
  178.     int offset;            /* not used */
  179.     Tcl_FreeProc **freeProcPtr;    /* not used */
  180. {
  181.     BusyWin *busyPtr = (BusyWin *)(widgRec);
  182.  
  183.     return (Tk_PathName(busyPtr->parent));
  184. }
  185.  
  186. /*
  187.  * ------------------------------------------------------------------
  188.  *
  189.  * HostWindowEventProc --
  190.  *
  191.  *    This procedure is invoked by the Tk dispatcher for the following
  192.  *    events on the host window.  If the host and parent windows are
  193.  *    the same, only the first event is important.
  194.  *
  195.  *       1) ConfigureNotify  - The host window has been resized or
  196.  *                 moved.  Move and resize the busy window
  197.  *                 to be the same size and position of the
  198.  *                 host window.
  199.  *
  200.  *       2) DestroyNotify    - The host window was destroyed. Destroy
  201.  *                 the busy window and the free resources
  202.  *                 used.
  203.  *
  204.  *       3) MapNotify           - The host window was (re)mapped. Map the
  205.  *                 busy window again.
  206.  *
  207.  *       4) UnmapNotify      - The host window was unmapped. Unmap the
  208.  *                 busy window.
  209.  *
  210.  * Results:
  211.  *    None.
  212.  *
  213.  * Side effects:
  214.  *    When the host window gets deleted, internal structures get
  215.  *     cleaned up.  When it gets resized, the busy window is resized
  216.  *    accordingly. If it's mapped, the busy window is mapped. And
  217.  *    when it's unmapped, the busy window is unmapped.
  218.  *
  219.  * -------------------------------------------------------------------
  220.  */
  221. static void
  222. HostWindowEventProc(clientData, eventPtr)
  223.     ClientData clientData;    /* Busy window record */
  224.     register XEvent *eventPtr;    /* Event which triggered call to routine */
  225. {
  226.     register BusyWin *busyPtr = (BusyWin *)clientData;
  227.  
  228.     if (eventPtr->type == DestroyNotify) {
  229.     /*
  230.      * Arrange for the busy structure to be removed at a proper time.
  231.      */
  232. #if (TK_MINOR_VERSION > 0)
  233.     Tk_EventuallyFree((ClientData)busyPtr, (Tcl_FreeProc *)DestroyBusyWindow);
  234. #else
  235.     Tk_EventuallyFree((ClientData)busyPtr, (Tk_FreeProc *)DestroyBusyWindow);
  236. #endif
  237.     } else if (eventPtr->type == ConfigureNotify) {
  238.     if ((busyPtr->width != Tk_Width(busyPtr->host)) ||
  239.         (busyPtr->height != Tk_Height(busyPtr->host)) ||
  240.         (busyPtr->hostX != Tk_X(busyPtr->host)) ||
  241.         (busyPtr->hostY != Tk_Y(busyPtr->host))) {
  242.         int x, y;
  243.  
  244.         busyPtr->width = Tk_Width(busyPtr->host);
  245.         busyPtr->height = Tk_Height(busyPtr->host);
  246.         busyPtr->hostX = Tk_X(busyPtr->host);
  247.         busyPtr->hostY = Tk_Y(busyPtr->host);
  248.  
  249.         x = y = 0;
  250.         if (busyPtr->parent != busyPtr->host) {
  251.         Tk_Window ancestor;
  252.  
  253.         for (ancestor = busyPtr->host; ancestor != busyPtr->parent;
  254.             ancestor = Tk_Parent(ancestor)) {
  255.             x += Tk_X(ancestor) + Tk_Changes(ancestor)->border_width;
  256.             y += Tk_Y(ancestor) + Tk_Changes(ancestor)->border_width;
  257.         }
  258.         }
  259.         if (busyPtr->window != None) {
  260.         XMoveResizeWindow(busyPtr->display, busyPtr->window, x, y,
  261.             busyPtr->width, busyPtr->height);
  262.         }
  263.         busyPtr->x = x;
  264.         busyPtr->y = y;
  265.     }
  266.     } else if (eventPtr->type == MapNotify) {
  267.     if ((busyPtr->parent != busyPtr->host) && (!busyPtr->mapped)) {
  268.         XMapRaised(busyPtr->display, busyPtr->window);
  269.         busyPtr->mapped = TRUE;
  270.     }
  271.     } else if (eventPtr->type == UnmapNotify) {
  272.     if ((busyPtr->parent != busyPtr->host) && (busyPtr->mapped)) {
  273.         XUnmapWindow(busyPtr->display, busyPtr->window);
  274.         busyPtr->mapped = FALSE;
  275.     }
  276.     }
  277. }
  278.  
  279. /*
  280.  * ------------------------------------------------------------------
  281.  *
  282.  * CreateBusyWindow --
  283.  *
  284.  *    Creates a child InputOnly window which obscures the parent
  285.  *      window thereby effectively blocking device events.  The size
  286.  *      and position of the busy window is exactly that of the host
  287.  *      window.
  288.  *
  289.  * Results:
  290.  *    Returns a pointer to the new busy window structure.
  291.  *
  292.  * Side effects:
  293.  *    When the busy window is eventually mapped, it will screen
  294.  *    device events (in the area of the host window) from reaching
  295.  *      its parent window and its children.  User feed back can be
  296.  *      achieved by changing the cursor.
  297.  *
  298.  * -------------------------------------------------------------------
  299.  */
  300. static BusyWin *
  301. CreateBusyWindow(interp, tkwin, searchWin)
  302.     Tcl_Interp *interp;        /* Interpreter to report error to */
  303.     Tk_Window tkwin;        /* Window hosting the busy window */
  304.     Tk_Window searchWin;    /* Window to use in searches of the parent
  305.                  * window by pathname */
  306. {
  307.     Tcl_HashEntry *entryPtr;
  308.     BusyWin *busyPtr;
  309.     XSetWindowAttributes attributes;
  310.     int dummy;
  311.  
  312.     busyPtr = (BusyWin *)malloc(sizeof(BusyWin));
  313.     if (busyPtr == NULL) {
  314.     interp->result = "can't allocate busy window";
  315.     return NULL;
  316.     }
  317.     busyPtr->host = busyPtr->parent = tkwin;
  318.     busyPtr->mainWin = searchWin;
  319.     busyPtr->cursor = None;
  320.     busyPtr->window = None;
  321.     busyPtr->display = Tk_Display(tkwin);
  322.     busyPtr->x = busyPtr->y = 0;
  323.  
  324.     /*
  325.      * Ignore the important events while the busy window is mapped.
  326.      */
  327.     attributes.do_not_propagate_mask = (KeyPressMask | KeyReleaseMask |
  328.     ButtonPressMask | ButtonReleaseMask | PointerMotionMask);
  329.     attributes.event_mask = (KeyPressMask | KeyReleaseMask |
  330.     ButtonPressMask | ButtonReleaseMask | PointerMotionMask);
  331.  
  332.     Tk_MakeWindowExist(tkwin);
  333.     /*
  334.      * Create an InputOnly window the size of the host window.
  335.      */
  336.     busyPtr->width = Tk_Width(tkwin);
  337.     busyPtr->height = Tk_Height(tkwin);
  338.     busyPtr->window = XCreateWindow(busyPtr->display, Tk_WindowId(tkwin),
  339.     busyPtr->x, busyPtr->y, busyPtr->width, busyPtr->height,
  340.     (unsigned int)0, CopyFromParent, InputOnly, CopyFromParent,
  341.     CWDontPropagate, &attributes);
  342.     if (busyPtr->cursor != None) {
  343.     XDefineCursor(busyPtr->display, busyPtr->window, busyPtr->cursor);
  344.     }
  345.     XMapRaised(busyPtr->display, busyPtr->window);
  346.     busyPtr->mapped = TRUE;
  347.     /*
  348.      * Track events in the host window to see if it is resized or destroyed.
  349.      */
  350.     Tk_CreateEventHandler(tkwin, StructureNotifyMask, HostWindowEventProc,
  351.     (ClientData)busyPtr);
  352.     entryPtr = Tcl_CreateHashEntry(&busyWinTable, (char *)tkwin, &dummy);
  353.     Tcl_SetHashValue(entryPtr, (char *)busyPtr);
  354.     return (busyPtr);
  355. }
  356.  
  357. /*
  358.  * ------------------------------------------------------------------
  359.  *
  360.  * DestoryBusyWindow --
  361.  *
  362.  *    This procedure is called from the Tk event dispatcher. It
  363.  *     releases X resources and memory used by the busy window and
  364.  *    updates the internal hash table.
  365.  *
  366.  * Results:
  367.  *    None.
  368.  *
  369.  * Side effects:
  370.  *    Memory and resources are released and the Tk event handler
  371.  *    is removed.
  372.  *
  373.  * -------------------------------------------------------------------
  374.  */
  375. static void
  376. DestroyBusyWindow(clientData)
  377.     ClientData clientData;    /* Busy window structure record */
  378. {
  379.     BusyWin *busyPtr = (BusyWin *)clientData;
  380.     Tcl_HashEntry *entryPtr;
  381.  
  382.     if (busyPtr->cursor != None) {
  383.     Tk_FreeCursor(busyPtr->display, busyPtr->cursor);
  384.     }
  385.     Tk_DeleteEventHandler(busyPtr->host, StructureNotifyMask,
  386.     HostWindowEventProc, (ClientData)busyPtr);
  387.     entryPtr = Tcl_FindHashEntry(&busyWinTable, (char *)busyPtr->host);
  388.     Tcl_DeleteHashEntry(entryPtr);
  389.     free((char *)busyPtr);
  390. }
  391.  
  392. /*
  393.  * ------------------------------------------------------------------
  394.  *
  395.  * GetBusyWindow --
  396.  *
  397.  *    Returns the busy window structure associated with the host
  398.  *      window, keyed by its path name.  The clientData argument is
  399.  *      the main window of the interpreter, used to search for the
  400.  *      host window in its own window hierarchy.
  401.  *
  402.  * Results:
  403.  *    If path name represents a host window with a busy window, a
  404.  *    pointer to the busy window structure is returned. Otherwise,
  405.  *    NULL is returned and an error message is left in
  406.  *    interp->result.
  407.  *
  408.  * -------------------------------------------------------------------
  409.  */
  410. static BusyWin *
  411. GetBusyWindow(clientData, interp, pathName)
  412.     ClientData clientData;    /* Window used to reference search  */
  413.     Tcl_Interp *interp;        /* Interpreter to report errors to */
  414.     char *pathName;        /* Path name of parent window */
  415. {
  416.     Tcl_HashEntry *entryPtr;
  417.     Tk_Window searchWin = (Tk_Window)clientData;
  418.     Tk_Window tkwin;
  419.  
  420.     tkwin = Tk_NameToWindow(interp, pathName, searchWin);
  421.     if (tkwin == NULL) {
  422.     return NULL;
  423.     }
  424.     entryPtr = Tcl_FindHashEntry(&busyWinTable, (char *)tkwin);
  425.     if (entryPtr == NULL) {
  426.     Tcl_AppendResult(interp, "can't find busy window \"", pathName, "\"",
  427.         (char *)NULL);
  428.     return NULL;
  429.     }
  430.     return ((BusyWin *)Tcl_GetHashValue(entryPtr));
  431. }
  432.  
  433. /*
  434.  * ------------------------------------------------------------------
  435.  *
  436.  * StatusBusyWindow --
  437.  *
  438.  *    Returns the status of the busy window; whether it's blocking
  439.  *    events or not.
  440.  *
  441.  * Results:
  442.  *    Returns a normal TCL result. If path name represents a busy
  443.  *    window, the status is returned via interp->result and TCL_OK
  444.  *    is returned. Otherwise, TCL_ERROR is returned and an error
  445.  *    message is left in interp->result.
  446.  *
  447.  * -------------------------------------------------------------------
  448.  */
  449. static int
  450. StatusBusyWindow(clientData, interp, pathName)
  451.     ClientData clientData;    /* Main window of interpreter */
  452.     Tcl_Interp *interp;        /* Interpreter to report error to */
  453.     char *pathName;        /* Path name of host window */
  454. {
  455.     BusyWin *busyPtr;
  456.  
  457.     busyPtr = GetBusyWindow(clientData, interp, pathName);
  458.     if (busyPtr == NULL) {
  459.     return TCL_ERROR;
  460.     }
  461.     Tk_Preserve((ClientData)busyPtr);
  462.     interp->result = (busyPtr->mapped) ? "1" : "0";
  463.     Tk_Release((ClientData)busyPtr);
  464.     return TCL_OK;
  465. }
  466.  
  467. /*
  468.  * ------------------------------------------------------------------
  469.  *
  470.  * ForgetBusyWindow --
  471.  *
  472.  *    Destroys the busy window associated with the host window and
  473.  *    arranges for internal resources to the released when they're
  474.  *    not being used anymore.
  475.  *
  476.  * Results:
  477.  *    Returns a normal TCL result. If path name represents a busy
  478.  *    window, it is destroyed and TCL_OK is returned. Otherwise,
  479.  *    TCL_ERROR is returned and an error message is left in
  480.  *    interp->result.
  481.  *
  482.  * Side effects:
  483.  *    The busy window is removed.  Other related memory and resources
  484.  *    are eventually released by the Tk dispatcher.
  485.  *
  486.  * -------------------------------------------------------------------
  487.  */
  488. static int
  489. ForgetBusyWindow(clientData, interp, pathName)
  490.     ClientData clientData;    /* Main window of the interpreter */
  491.     Tcl_Interp *interp;        /* Interpreter to report errors to */
  492.     char *pathName;        /* Path name of host window */
  493. {
  494.     BusyWin *busyPtr;
  495.  
  496.     busyPtr = GetBusyWindow(clientData, interp, pathName);
  497.     if (busyPtr == NULL) {
  498.     return TCL_ERROR;
  499.     }
  500.     Tk_Preserve((ClientData)busyPtr);
  501.     if (busyPtr->window != None) {
  502.     XDestroyWindow(busyPtr->display, busyPtr->window);
  503.     busyPtr->window = None;
  504.     }
  505. #if (TK_MINOR_VERSION > 0)
  506.     Tk_EventuallyFree((ClientData)busyPtr, (Tcl_FreeProc *)DestroyBusyWindow);
  507. #else
  508.     Tk_EventuallyFree((ClientData)busyPtr, (Tk_FreeProc *)DestroyBusyWindow);
  509. #endif
  510.     Tk_Release((ClientData)busyPtr);
  511.     return TCL_OK;
  512. }
  513.  
  514. /*
  515.  * ------------------------------------------------------------------
  516.  *
  517.  * ReleaseBusyWindow --
  518.  *
  519.  *    Unmaps the busy window, thereby permitting device events
  520.  *    to be received by the parent window and its children.
  521.  *
  522.  * Results:
  523.  *    Returns a normal TCL result. If path name represents a busy
  524.  *    window, it is unmapped and TCL_OK is returned. Otherwise,
  525.  *    TCL_ERROR is returned and an error message is left in
  526.  *    interp->result.
  527.  *
  528.  * Side effects:
  529.  *    The busy window is unmapped, allowing the parent window and
  530.  *    its children to receive events again.
  531.  *
  532.  * -------------------------------------------------------------------
  533.  */
  534. static int
  535. ReleaseBusyWindow(clientData, interp, pathName)
  536.     ClientData clientData;    /* Main window of the interpreter */
  537.     Tcl_Interp *interp;        /* Interpreter to report errors to */
  538.     char *pathName;        /* Path name of host window */
  539. {
  540.     BusyWin *busyPtr;
  541.  
  542.     busyPtr = GetBusyWindow(clientData, interp, pathName);
  543.     if (busyPtr == NULL) {
  544.     return TCL_ERROR;
  545.     }
  546.     Tk_Preserve((ClientData)busyPtr);
  547.     if ((busyPtr->mapped) && (busyPtr->window != None)) {
  548.     busyPtr->mapped = FALSE;
  549.     XUnmapWindow(busyPtr->display, busyPtr->window);
  550.     }
  551.     Tk_Release((ClientData)busyPtr);
  552.     return TCL_OK;
  553. }
  554.  
  555. /*
  556.  * ------------------------------------------------------------------
  557.  *
  558.  * HoldBusyWindow --
  559.  *
  560.  *    Creates (if necessary) and maps a busy window, thereby
  561.  *    preventing device events from being be received by the parent
  562.  *      window and its children.
  563.  *
  564.  * Results:
  565.  *    Returns a normal TCL result. If path name represents a busy
  566.  *    window, it is unmapped and TCL_OK is returned. Otherwise,
  567.  *    TCL_ERROR is returned and an error message is left in
  568.  *    interp->result.
  569.  *
  570.  * Side effects:
  571.  *    The busy window is created and mapped, blocking events from the
  572.  *    parent window and its children.
  573.  *
  574.  * -------------------------------------------------------------------
  575.  */
  576. static int
  577. HoldBusyWindow(clientData, interp, argc, argv)
  578.     ClientData clientData;    /* Main window of the interpreter */
  579.     Tcl_Interp *interp;        /* Interpreter to report errors to */
  580.     int argc;
  581.     char **argv;        /* Window name and option pairs */
  582. {
  583.     Tk_Window searchWin = (Tk_Window)clientData;
  584.     Tk_Window tkwin;
  585.     Tcl_HashEntry *entryPtr;
  586.     BusyWin *busyPtr;
  587.     int result;
  588.  
  589.     tkwin = Tk_NameToWindow(interp, argv[0], searchWin);
  590.     if (tkwin == NULL) {
  591.     return TCL_ERROR;
  592.     }
  593.     entryPtr = Tcl_FindHashEntry(&busyWinTable, (char *)tkwin);
  594.     if (entryPtr == NULL) {
  595.     busyPtr = (BusyWin *)CreateBusyWindow(interp, tkwin, searchWin);
  596.     } else {
  597.     busyPtr = (BusyWin *)Tcl_GetHashValue(entryPtr);
  598.     }
  599.     if (busyPtr == NULL) {
  600.     return TCL_ERROR;
  601.     }
  602.     Tk_Preserve((ClientData)busyPtr);
  603.     result = Tk_ConfigureWidget(interp, tkwin, configSpecs, argc - 1, argv + 1,
  604.     (char *)busyPtr, 0);
  605.     if ((result == TCL_OK) && (busyPtr->window != None)) {
  606.     /*
  607.      * Define the new cursor and map the busy window
  608.      */
  609.     if (busyPtr->cursor != None) {
  610.         XDefineCursor(busyPtr->display, busyPtr->window, busyPtr->cursor);
  611.     }
  612.     XMapRaised(busyPtr->display, busyPtr->window);
  613.     busyPtr->mapped = TRUE;
  614.     }
  615.     Tk_Release((ClientData)busyPtr);
  616.     return result;
  617. }
  618.  
  619. /*
  620.  *----------------------------------------------------------------------
  621.  *
  622.  * ConfigureBusyWindow --
  623.  *
  624.  *    This procedure is called to process an argv/argc list
  625.  *    in order to configure (or reconfigure) a busy window.
  626.  *
  627.  * Results:
  628.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  629.  *    returned, then interp->result contains an error message.
  630.  *
  631.  * Side effects:
  632.  *    Configuration information get set for busyPtr;  old resources
  633.  *      get freed, if there were any.  The busy window may be reparented
  634.  *      to a new parent window.
  635.  *
  636.  *----------------------------------------------------------------------
  637.  */
  638. static int
  639. ConfigureBusyWindow(clientData, interp, argc, argv)
  640.     ClientData clientData;    /* Main window of the interpreter */
  641.     Tcl_Interp *interp;        /* Interpreter to report errors to */
  642.     int argc;
  643.     char **argv;        /* Host window path name and options */
  644. {
  645.     BusyWin *busyPtr;
  646.     int result;
  647.  
  648.     busyPtr = GetBusyWindow(clientData, interp, argv[1]);
  649.     if (busyPtr == NULL) {
  650.     return TCL_ERROR;
  651.     }
  652.     Tk_Preserve((ClientData)busyPtr);
  653.     if (argc == 2) {
  654.     result = Tk_ConfigureInfo(interp, busyPtr->host, configSpecs,
  655.         (char *)busyPtr, (char *)NULL, 0);
  656.     } else if (argc == 3) {
  657.     result = Tk_ConfigureInfo(interp, busyPtr->host, configSpecs,
  658.         (char *)busyPtr, argv[2], 0);
  659.     } else {
  660.     Cursor oldCursor;
  661.  
  662.     oldCursor = busyPtr->cursor;
  663.     result = Tk_ConfigureWidget(interp, busyPtr->host, configSpecs,
  664.         argc - 2, argv + 2, (char *)busyPtr, TK_CONFIG_ARGV_ONLY);
  665.     if ((result == TCL_OK) && (busyPtr->window != None)) {
  666.         /* Set cursor on busy window */
  667.         if (busyPtr->cursor != None) {
  668.         XDefineCursor(busyPtr->display, busyPtr->window,
  669.             busyPtr->cursor);
  670.         } else if (oldCursor != None) {
  671.         XUndefineCursor(busyPtr->display, busyPtr->window);
  672.         }
  673.     }
  674.     }
  675.     Tk_Release((ClientData)busyPtr);
  676.     return result;
  677. }
  678.  
  679. /*
  680.  *----------------------------------------------------------------------
  681.  *
  682.  * BusyCmd --
  683.  *
  684.  *    This procedure is invoked to process the "busy" Tcl command.
  685.  *    See the user documentation for details on what it does.
  686.  *
  687.  * Results:
  688.  *    A standard Tcl result.
  689.  *
  690.  * Side effects:
  691.  *    See the user documentation.
  692.  *
  693.  *----------------------------------------------------------------------
  694.  */
  695. static int
  696. BusyCmd(clientData, interp, argc, argv)
  697.     ClientData clientData;    /* Main window of the interpreter */
  698.     Tcl_Interp *interp;        /* Interpreter associated with command */
  699.     int argc;
  700.     char **argv;
  701. {
  702.     char c;
  703.     int length;
  704.  
  705.     if (!initialized) {
  706.     Tcl_InitHashTable(&busyWinTable, TCL_ONE_WORD_KEYS);
  707.     initialized = 1;
  708.     }
  709.     if (argc < 2) {
  710.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  711.         " option window\"", (char *)NULL);
  712.     return TCL_ERROR;
  713.     }
  714.     c = argv[1][0];
  715.     length = strlen(argv[1]);
  716.     if ((c == '.') ||
  717.     ((c == 'h') && (strncmp(argv[1], "hold", length) == 0))) {
  718.     register int i, count;
  719.     char *savePtr;
  720.  
  721.     if (c == 'h') {
  722.         if (argc < 3) {
  723.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  724.             " hold window ?options...?\"", (char *)NULL);
  725.         return TCL_ERROR;
  726.         }
  727.         argc--, argv++;
  728.     }
  729.     for (i = 1; i < argc; i++) {
  730.         /*
  731.          * Find the end of the option-value pairs for this window.
  732.          */
  733.         for (count = i + 1; count < argc; count += 2) {
  734.         if (argv[count][0] != '-') {
  735.             break;
  736.         }
  737.         }
  738.         savePtr = argv[count];
  739.         argv[count] = NULL;
  740.         if (HoldBusyWindow(clientData, interp,
  741.             count - i, argv + i) != TCL_OK) {
  742.         return TCL_ERROR;
  743.         }
  744.         argv[count] = savePtr;
  745.         i = count;
  746.     }
  747.     return TCL_OK;
  748.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)) {
  749.     if (argc < 3) {
  750.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  751.         " configure window ?options?\"", (char *)NULL);
  752.         return TCL_ERROR;
  753.     }
  754.     return (ConfigureBusyWindow(clientData, interp, argc - 1, argv + 1));
  755.     } else if ((c == 'r') && (strncmp(argv[1], "release", length) == 0)) {
  756.     register int i;
  757.  
  758.     if (argc < 3) {
  759.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  760.         " release window ?window ...?\"", (char *)NULL);
  761.         return TCL_ERROR;
  762.     }
  763.     for (i = 2; i < argc; i++) {
  764.         if (ReleaseBusyWindow(clientData, interp, argv[i]) != TCL_OK) {
  765.         return TCL_ERROR;
  766.         }
  767.     }
  768.     return TCL_OK;
  769.     } else if ((c == 'f') && (strncmp(argv[1], "forget", length) == 0)) {
  770.     register int i;
  771.  
  772.     if (argc < 3) {
  773.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  774.         " forget window ?window ...?\"", (char *)NULL);
  775.         return TCL_ERROR;
  776.     }
  777.     for (i = 2; i < argc; i++) {
  778.         if (ForgetBusyWindow(clientData, interp, argv[i]) != TCL_OK) {
  779.         return TCL_ERROR;
  780.         }
  781.     }
  782.     return TCL_OK;
  783.     } else if ((c == 'h') && (strncmp(argv[1], "hosts", length) == 0)) {
  784.     Tk_Window searchWin = (Tk_Window)clientData;
  785.     Tcl_HashEntry *entryPtr;
  786.     Tcl_HashSearch cursor;
  787.     BusyWin *busyPtr;
  788.  
  789.     if (argc > 3) {
  790.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  791.         " hosts ?pattern?\"", (char *)NULL);
  792.         return TCL_ERROR;
  793.     }
  794.     for (entryPtr = Tcl_FirstHashEntry(&busyWinTable, &cursor);
  795.         entryPtr != NULL; entryPtr = Tcl_NextHashEntry(&cursor)) {
  796.         busyPtr = (BusyWin *)Tcl_GetHashValue(entryPtr);
  797.         if (busyPtr->mainWin == searchWin) {
  798.         if ((argc != 3) ||
  799.             (Tcl_StringMatch(Tk_PathName(busyPtr->host), argv[2]))) {
  800.             Tcl_AppendElement(interp, Tk_PathName(busyPtr->host));
  801.         }
  802.         }
  803.     }
  804.     return TCL_OK;
  805.     } else if ((c == 's') && (strncmp(argv[1], "status", length) == 0)) {
  806.     if (argc < 3) {
  807.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  808.         " status window\"", (char *)NULL);
  809.         return TCL_ERROR;
  810.     }
  811.     return (StatusBusyWindow(clientData, interp, argv[2]));
  812.     } else {
  813.     Tcl_AppendResult(interp, "bad option \"", argv[1], "\" should be ",
  814.         "configure, forget, hold, hosts, or release", (char *)NULL);
  815.     return TCL_ERROR;
  816.     }
  817. }
  818.  
  819. int
  820. Blt_BusyInit(interp)
  821.     Tcl_Interp *interp;
  822. {
  823.     Tk_Window tkwin;
  824.  
  825.     if (Blt_FindCmd(interp, "blt_busy", (ClientData *)NULL) == TCL_OK) {
  826.     Tcl_AppendResult(interp, "\"blt_busy\" command already exists",
  827.         (char *)NULL);
  828.     return TCL_ERROR;
  829.     }
  830.     tkwin = Tk_MainWindow(interp);
  831.     if (tkwin == NULL) {
  832.     Tcl_AppendResult(interp, "\"blt_busy\" requires Tk", (char *)NULL);
  833.     return TCL_ERROR;
  834.     }
  835.     Tcl_SetVar2(interp, "blt_versions", "blt_busy", BUSY_VERSION,
  836.     TCL_GLOBAL_ONLY);
  837.     Tcl_CreateCommand(interp, "blt_busy", BusyCmd, (ClientData)tkwin,
  838.     (Tcl_CmdDeleteProc *)NULL);
  839.     return TCL_OK;
  840. }
  841.